﻿using System;
using HK.Core.Common;
using HK.Core.Counter.Protocol;

namespace HK.Core.Counter {

	/// <summary>
	/// 计数器基类
	/// </summary>
	/// <typeparam name="TType">值类型</typeparam>
	public abstract class CounterBase<TType> : ClassExtension, ICounter<TType> 
	{
		
		/// <summary>
		/// 委托定义 - 计数器更新事件
		/// </summary>
		/// <param name="iCurValue">当前值</param>
		/// <param name="iDstValue">目标值</param>
		/// <param name="iType">计数器类型</param>
		/// <param name="iState">计数器状态</param>
		/// <param name="iLoop">循环标志位</param>
		public delegate void CounterUpdated(
			TType iCurValue, TType iDstValue, CounterType iType, 
			CounterState iState, bool iLoop);

#region Status
		
		/// <summary>
		/// 类型.
		/// </summary>
		public CounterType Type { get; protected set; } = CounterType.NumberCounter;

		/// <summary>
		/// 模式.
		/// </summary>
		public CounterMode Mode { get; protected set; } = CounterMode.CountDown;

		/// <summary>
		/// 模式.
		/// </summary>
		public CounterState State { get; protected set; } = CounterState.None;

		/// <summary>
		/// 更新时间间隔<BR/>
		/// (单位:100毫微秒)
		/// </summary>
		public long Interval { get; protected set; } = 0L;

		/// <summary>
		/// 开始系统时钟数<BR/>
		/// (单位:100毫微秒)
		/// </summary>
		protected long StartTicks = 0L;
		
		/// <summary>
		/// 上一次更新的时钟(单位:100毫微秒)
		/// </summary>
		protected long LastUpdateTime = 0L;

		/// <summary>
		/// 变化时间(单位:100毫微秒)
		/// </summary>
		public long DeltaTime
		{
			get
			{
				var delTime = 0L;
				if (0L >= StartTicks) return delTime;
				delTime = DateTime.Now.Ticks - StartTicks;
				return 0L >= delTime ? 0L : delTime;
			}
		}

		/// <summary>
		/// 空闲标志位.
		/// </summary>
		public bool IsIdle => CounterState.Idle == State;

		/// <summary>
		/// 计数标志位
		/// </summary>
		public bool IsCounting => CounterState.Counting == State;

		/// <summary>
		/// 循环计数标志位.
		/// </summary>
		public bool Loop { get; protected set; } = false;
        
		/// <summary>
		/// 暂停标志位
		/// </summary>
		public bool Pausing { get; protected set; } = false;
        
#endregion

#region Action
	
		/// <summary>
		/// 开始计数器.
		/// </summary>
		public abstract void Start ();
        
		/// <summary>
		/// 暂停计数器.
		/// </summary>
		public void Pause()
		{
			Pausing = true;
		}

		/// <summary>
		/// 回复/继续
		/// </summary>
		public void Resume()
		{
			Pausing = false;
		}

		/// <summary>
		/// 结束计数器.
		/// </summary>
		public abstract void End ();

		/// <summary>
		/// 重置计数器.
		/// </summary>
		public abstract void Reset ();

		/// <summary>
		/// 重启计数器.
		/// </summary>
		public abstract void Restart ();

#endregion

#region Values
        
		/// <summary>
		/// 目标值.
		/// </summary>
		public TType DstValue { get; protected set; } = default(TType);

		/// <summary>
		/// 当前值.
		/// </summary>
		public TType Value { get; protected set; } = default(TType);

		/// <summary>
		/// 重置最大值
		/// </summary>
		/// <param name="iValue">值</param>
		public void ResetMaxValue(TType iValue) => DstValue = iValue;

		/// <summary>
		/// 更新计数器.
		/// </summary>
		/// <param name="iDeltaValue">变化值.</param>
		/// <returns><c>true</c>, 已经溢出, <c>false</c> 尚未溢出.</returns>
		public abstract bool Update (TType iDeltaValue);

		/// <summary>
		/// 取得计数器值（根据类型不同，格式也不同）.
		/// </summary>
		public abstract string GetValue ();

#endregion

#region Virtual

		/// <summary>
		/// 释放
		/// </summary>
		public virtual void Dispose()
		{
			End();
		}

#endregion

#region Abstract

		/// <summary>
		/// 初始化计数器.
		/// </summary>
		/// <param name="iType">类型.</param>
		/// <param name="iDstValue">目标值.</param>
		/// <param name="iOnCounterUpdated">计数更新回调.</param>
		/// <param name="iMode">模式.</param>
		/// <param name="iLoop">循环计数标志位.</param>
		/// <param name="iInterval">更新时间间隔(单位:100毫微秒).</param>
		protected abstract void Init(
			CounterType iType, TType iDstValue, 
			CounterUpdated iOnCounterUpdated = null, CounterMode iMode = CounterMode.CountDown,
			bool iLoop = false, long iInterval = 100L);

#endregion

	}

	/// <summary>
	/// 计数器基类
	/// </summary>
	/// <typeparam name="TCounter">计数器类型</typeparam>
	/// <typeparam name="TType">值类型</typeparam>
	public abstract class Counter<TCounter, TType> : CounterBase<TType>
		where TCounter : Counter<TCounter, TType>, new()
	{
		
		/// <summary>
		/// 创建计数器.
		/// </summary>
		/// <param name="iType">计数器类型.</param>
		/// <param name="iDstValue">目标值.</param>
		/// <param name="iOnCounterUpdate">计时器更新回调.</param>
		/// <param name="iMode">模式（默认为：倒计时）.</param>
		/// <param name="iLoop">循环计数标志位.</param>
		/// <param name="iInterval">更新时间间隔(单位:100毫微秒).</param>
		protected static TCounter Create(
			CounterType iType, TType iDstValue, 
			CounterUpdated iOnCounterUpdate, 
			CounterMode iMode = CounterMode.CountDown, 
			bool iLoop = false, long iInterval = 100L) {
		
			var objRet = new TCounter ();
			objRet.Init (iType, iDstValue, iOnCounterUpdate, iMode, iLoop, iInterval);
			return objRet;
		}

		private CounterUpdated _counterUpdated = (iCurCount, iDstCount, iType, iState, iLoop) => {};
		/// <summary>
		/// 计数更新回调函数.
		/// </summary>
		protected event CounterUpdated OnCounterUpdated {
			add => _counterUpdated += value;
			remove => _counterUpdated = value;
		}

#region Abstract

		/// <summary>
		/// 是否已经超过计数.
		/// </summary>
		/// <returns><c>true</c>, 已经超过计数, <c>false</c> 尚未超过计数.</returns>
		protected abstract bool IsCountOver();

		/// <summary>
		/// 根据变化值更新计数器.
		/// </summary>
		/// <param name="iDeltaValue">变化值.</param>
		protected abstract void UpdateCounter(TType iDeltaValue);

#endregion

#region Implement

		/// <summary>
		/// 初始化计数器.
		/// </summary>
		/// <param name="iType">类型.</param>
		/// <param name="iDstValue">目标值.</param>
		/// <param name="iOnCounterUpdated">计数更新回调.</param>
		/// <param name="iMode">模式.</param>
		/// <param name="iLoop">循环计数标志位.</param>
		/// <param name="iInterval">更新时间间隔(单位:100毫微秒).</param>
		protected override void Init(
			CounterType iType, TType iDstValue, 
			CounterUpdated iOnCounterUpdated = null,
			CounterMode iMode = CounterMode.CountDown, 
			bool iLoop = false, long iInterval = 100L) {
			
			// 目标值
			DstValue = iDstValue;
			// 类型
			Type = iType;
			// 模式
			Mode = iMode;
			// 循环计数标志位
			Loop = iLoop;
			// 暂停标志位
			Pausing = false;
			
			// 默认更新时间间隔为0
			Interval = iInterval;
			
			State = CounterState.Idle;
			switch (Mode) {
			case CounterMode.CountDown:
				{
					Value = DstValue;
				}
				break;
			case CounterMode.CountUp:
				{
					Value = default(TType);
				}
				break;
			default:
				break;
			}
			
			// 计数结束回调
			if (null != iOnCounterUpdated)
			{
				OnCounterUpdated += iOnCounterUpdated;
			}
		}

		/// <summary>
		/// 开始计数器.
		/// </summary>
		public override void Start () {
			State = CounterState.Counting;
			// 暂停标志位
			Pausing = false;
			// 记录开始时钟数（单位:毫微秒）
			StartTicks = DateTime.Now.Ticks;
			// Info ("StartCounter():Type::{0} Mode::{1} State::{2} Value::{3}/{4}",
			// 	Type, Mode, State, Value, DstValue);
		}

		/// <summary>
		/// 更新计数器.
		/// </summary>
		/// <param name="iDeltaValue">变化值.</param>
		/// <returns>true:计数结束; false:尚未结束;</returns>
		public override bool Update (TType iDeltaValue) {

			// 若当前未暂停
			if (Pausing) return false;
			
			// 尚未开始计数
			if (CounterState.Counting != State) {
				return false;
			}
			// 更新当前状态为：计数中
			State = CounterState.Counting;
			// 根据变化值更新计数器
			UpdateCounter (iDeltaValue);
			
			// 超过计数
			if (IsCountOver())
			{
				// 非循环计数，则结束
				if (!Loop)
				{
					End ();
					return true;
				}
				
				// 一次循环计数达标，更新一次回调
				_counterUpdated (Value, DstValue, Type, CounterState.End, Loop);
				// 循环计数，则重启计数器
				Restart();
				return false;
			}

			// 临时保存更新时间
			var curUpdateTime = DateTime.Now.Ticks;
			if (0L == LastUpdateTime)
			{
				LastUpdateTime = DateTime.Now.Ticks;
			}
			// 计算前后更新变化时间，若超过更新时间间隔，则更新
			var deltaUpdateTime = curUpdateTime - LastUpdateTime;
			if (Interval >= deltaUpdateTime)
			{
				return false;
			}
			LastUpdateTime = curUpdateTime;

			// 回调更新
			_counterUpdated (Value, DstValue, Type, State, Loop);
			return false;
		}

		/// <summary>
		/// 结束计数器.
		/// </summary>
		public override void End () {
			
			// 暂停标志位
			Pausing = false;
			// Info ("EndCounter():Type::{0} Mode::{1} State::{2} -> {3} Value::{4}/{5}",
			// 	Type, Mode, State, CounterState.End, Value, DstValue);
			State = CounterState.End;
			_counterUpdated(Value, DstValue, Type, State, Loop);
		}

		/// <summary>
		/// 重置计数器.
		/// </summary>
		public override void Reset () {
			// 暂停标志位
			Pausing = false;
			Init (Type, DstValue, null, Mode, Loop);
		}
			
		/// <summary>
		/// 重启计数器.
		/// </summary>
		public override void Restart () {
			// 重置计数器
			Reset();
			// 开始计数
			Start();
			// Info ("RestartCounter():Type::{0} Mode::{1} State::{2} Value::{3}/{4}",
			// 	Type, Mode, State, Value, DstValue);
		}

		/// <summary>
		/// 取得计数器值（根据类型不同，格式也不同）.
		/// </summary>
		public override string GetValue () {
			return Value.ToString();
		}

#endregion
	}
}
